この記事は, KobeUniv Advent Calender 2015 13日目として書かれたものです。
低レイヤーの人とか真の低レイヤーの人とか、とにかく低レイヤーな人がいっぱいいたのでそれに乗っかってFPGAの入門記的な記事を書くことにしました。完全にFPGA初心者ですが…。(もうちょっと先にFPGAの話をするという強そうな人がいるので期待しています。)
FPGAとは?
field-programmable gate array の略らしい。英語そのままですが、プログラミングするだけで論理回路の配線を勝手にやってくれるic的なイメージです。ブレッドボードの配線をプログラミングでできる感じというと分かりやすい? これを使うとお手軽にCPUとかが作れます。詳しくはWikipedia
ハードウェア記述言語
FPGA上に回路を作るにはその回路の設計書を書かないといけません。それにはハードウェア記述言語という専用の言語を使います。いくつか種類があってVerilogやVHDLが有名なようです。C言語っぽいSystemCとかいうのもあります。自分は有名どころであるVerilog,VHDL両方触ってみたのですが、今回はシミュレータの関係と、わかりやすさからVerilogで書きます。
シミュレータ
VerilogにはIcarus Verilogという有名なシミュレータがあります。Windows,Mac,Linuxどれでも動きます。Verilogを書いてIcarus Verilogに渡すと波形データを出力してくれてそれをgtkwaveでみたりできます。web上で動くシミュレータもあります。
Verilogのプログラム
下は4bitFullAdderのプログラム例。+演算子を普通に使ってるのであまり楽しくありませんが。
演算子はc並みに使えてしまうので、それを考えるとbrainf*ckとかよりははるかに高級。
module adder4bit (in1, in2, out, carry);
input [3:0] in1, in2;
output [3:0] out;
output carry;
wire [4:0] result;
assign result = in1 + in2;
assign carry = result[4];
assign out = result[3:0];
endmodule // adder4bit
シミュレーションプログラムを書いて実行すると
module test();
reg [3:0] in1, in2;
wire [3:0] result;
wire carry;
adder4bit adder4bit(in1, in2, result, carry);
initial begin
$monitor("%t : %b + %b => %b, %b", $time, in1, in2, carry, result);
end
initial begin
in1 <= 4'b0000;
in2 <= 4'b0000;
#1
in1 <= 4'b0001;
in2 <= 4'b0010;
#1
in1 <= 4'b0001;
in2 <= 4'b0001;
#1
in1 <= 4'b0101;
in2 <= 4'b0111;
#1
in1 <= 4'b1111;
in2 <= 4'b1111;
#1
$finish;
end
endmodule
出力は以下のようになり、ちゃんと足してくれています。
$ iverilog main.v test.v
$ ./a.out
0 : 0000 + 0000 => 0, 0000
1 : 0001 + 0010 => 0, 0011
2 : 0001 + 0001 => 0, 0010
3 : 0101 + 0111 => 0, 1100
4 : 1111 + 1111 => 1, 1110
簡単ですね。
なに作ろうか..
低レイヤーに乗っかったのはいいものの、あまり触っていないのでこれ作ったよ!的なものがない…。FPGAに手を出した目的はもちろんCPUを作ることです。実は自分にはトランジスタで論理回路作ろうとか、ICでCPUつくろうとか過去に色々策略を立てては失敗したり気が変わったりしてきた歴史があるのですが、今回こそは作ります。CPUを作るというと、「CPUの創りかた」という有名な本があり、自分も過去に読んだことがあるのですが、この本で紹介されているTD4というCPUならサクッと作れそうです 。(今回作ろうと思っていたが、圧倒的に時間がなかった & 本が借りられなかったので延期)また、有名な話ですが、東大ではFPGAを使ってCPUからコンパイラ、アセンブリまで作る実験があるそうです。コンパイラのコード生成とかの部分はまだ手を出せていませんが興味はあるのでやってみたいですね。あと、最近Prologを習って、WAMというものを知ったのですが、それも面白そうです。大学に飾られてるLispマシンとかも。
で、実はTD4よりはちゃんとした(?)CPUを作ろうと今勉強中なのですが、それはまた今度。何を書こうか考えたのですが、時間もなかったので、すぐに書けるものということで電子サイコロを書いておきました。
電子サイコロ
なぜ電子サイコロ?…実は情報知能工学科二回後期には情報知能工学実験という名のLEGO遊び & ひたすら半田付け の実験があるのですが、半田付けの方の選択課題の一つとして電子サイコロを作れというものがあります。先日ちょうど作ってきました(90分x6コマ)。それにのっける感じで電子サイコロを書くことにしたのですが、半田付けなら540分もかかる回路も、FPGAなら数分で書けます!とりあえずなんか書いた程度なので色々ガバガバです。入力が止まってからしばらく動き続ける仕様。書き終えてからランダム性が全くないことに気づいたのですがまあよしとします。
`default_nettype none
module dice(clock, reset, in, seg);
input clock, reset, in;
output [6:0] seg;
output delayed_in;
output [2:0] bin;
wire [3:0] bin2;
assign bin2 = {1'b0,bin};
inputmodule inputmodule(clock, in, delayed_in);
counter1_6 counter1_6(clock & delayed_in, reset, bin);
bcd7seg bcd7seg(bin2, seg);
endmodule // dice
module inputmodule(clock, in, out);
input in, clock;
output out;
reg [31:0] q;
assign out = in | q[31];
always @(posedge in or posedge clock)
begin
if( in ) q <= ~32'b0;
else q <= q << 1;
end
endmodule // inputmodule
// counter from 1 to 6
module counter1_6(clock, reset, q);
input clock,reset;
output [2:0] q;
reg [2:0] q;
always @(posedge clock or posedge reset)
begin
if( reset ) q <= 3'd1;
else if( q == 3'd6 ) q <= 3'd1;
else q <= q + 3'd1;
end
endmodule // counter1_6
// binary to 7segLED
module bcd7seg(in, seg);
input [3:0] in;
output [6:0] seg;
assign seg = decode(in);
function [6:0] decode;
input [3:0] in;
begin
if(in == 0) decode = 7'b1111110;
else if(in == 1) decode = 7'b0110000;
else if(in == 2) decode = 7'b1101101;
else if(in == 3) decode = 7'b1111001;
else if(in == 4) decode = 7'b0110011;
else if(in == 5) decode = 7'b1011011;
else if(in == 6) decode = 7'b1011111;
else if(in == 7) decode = 7'b1110000;
else if(in == 8) decode = 7'b1111111;
else if(in == 9) decode = 7'b1111011;
else decode = 7'b0110111;
end
endfunction
endmodule
HelloWorld代わりに書いたレベルなので実機で動くか不明& 色々変なところがあるとは思いますが、とりあえずシミュレータでは動いています。
$ iverilog main.v test.v
$ ./a.out
Time:clock,in,7segout
0 : 0 0 => 0110000
5 : 1 0 => 0110000
10 : 0 0 => 0110000
15 : 1 0 => 1101101
20 : 0 1 => 1101101
25 : 1 1 => 1111001
30 : 0 0 => 1111001
35 : 1 0 => 0110011
40 : 0 0 => 0110011
45 : 1 0 => 1011011
50 : 0 0 => 1011011
55 : 1 0 => 1011111
60 : 0 0 => 1011111
65 : 1 0 => 0110000
70 : 0 0 => 0110000
75 : 1 0 => 1101101
80 : 0 0 => 1101101
85 : 1 0 => 1111001
90 : 0 0 => 1111001
95 : 1 0 => 0110011
100 : 0 0 => 0110011
105 : 1 0 => 1011011
110 : 0 0 => 1011011
115 : 1 0 => 1011111
120 : 0 0 => 1011111
125 : 1 0 => 0110000
130 : 0 0 => 0110000
135 : 1 0 => 1101101
140 : 0 0 => 1101101
145 : 1 0 => 1111001
150 : 0 0 => 1111001
155 : 1 0 => 0110011
160 : 0 0 => 0110011
165 : 1 0 => 1011011
170 : 0 0 => 1011011
175 : 1 0 => 1011111
180 : 0 0 => 1011111
185 : 1 0 => 0110000
190 : 0 0 => 0110000
195 : 1 0 => 1101101
200 : 0 0 => 1101101
205 : 1 0 => 1111001
210 : 0 0 => 1111001
215 : 1 0 => 0110011
220 : 0 0 => 0110011
225 : 1 0 => 1011011
230 : 0 0 => 1011011
235 : 1 0 => 1011111
240 : 0 0 => 1011111
245 : 1 0 => 0110000
250 : 0 0 => 0110000
255 : 1 0 => 1101101
260 : 0 0 => 1101101
265 : 1 0 => 1111001
270 : 0 0 => 1111001
275 : 1 0 => 0110011
280 : 0 0 => 0110011
285 : 1 0 => 1011011
290 : 0 0 => 1011011
295 : 1 0 => 1011111
300 : 0 0 => 1011111
305 : 1 0 => 0110000
310 : 0 0 => 0110000
315 : 1 0 => 1101101
320 : 0 0 => 1101101
325 : 1 0 => 1111001
330 : 0 0 => 1111001
335 : 1 0 => 0110011
340 : 0 0 => 0110011
345 : 1 0 => 1011011
350 : 0 0 => 1011011
355 : 1 0 => 1011011
360 : 0 0 => 1011011
365 : 1 0 => 1011011
370 : 0 0 => 1011011
375 : 1 0 => 1011011
380 : 0 0 => 1011011
385 : 1 0 => 1011011
390 : 0 0 => 1011011
395 : 1 0 => 1011011
400 : 0 0 => 1011011
動かせる実機は持っているのですがまだ動かしていません、動かしてみたら追記する予定。TOEICが迫っているのでこの辺で。